#include <mpi.h>
#include <omp.h>
#include "processaFrase.h"

/* Ajusta o offset do arquivo até o próximo caracter que não seja letra */
long ajustaOffset(FILE *fp){
  char c;
  do {
    c = fgetc(fp);
  } while (isalpha(c));
  
  return ftell(fp); 
}


/* Função main */
int main(int argc, char* argv[]){
  /* Ponteiro para o arquivo */
  FILE *fp;
  
  /* Variáveis para frase e palavra */
  char buffer[TAM_FRASE], palavra[TAM_PALAVRA];
  /* Variável para percorer as letras da frase */
  int percorre_frase;
  
  /* Offset para início e fim de leitura do arquivo */
  long posInit, posEnd, posAux;
  
  /* Variáveis de contagem */
  int globalPalindromos, partialPalindromos = 0;
  int globalFrasePalindromos, partialFrasePalindromos = 0;
  
  /* Variáveis para registrar o tempo de início e término de execução */
  double start, end;
      
  /* Variável para verificação da inicialização do MPI e das leituras do arquivo (EOF) */
  int ret = 0;
  
  /* Variáveis do MPI */
  int rank, nthreads;
  MPI_Status status;
    
  /* Inicializa o MPI */
  ret = MPI_Init(&argc, &argv);
  if(ret != MPI_SUCCESS){
      printf("Erro na inicialização da execução paralela com o MPI");
      return -1;
  }
  
  /* Funções para pegar o número de threads e o rank do processo */
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &nthreads);
  
  /* Registra o tempo de início */
  start = MPI_Wtime();
  
  /*=========================================================================================*/  
  /* Cada processo lerâ de uma posição do arquivo */
  fp = fopen("shakespe.are", "r");
  if(fp < 0){
    printf("Arquivo não encontrado\n");
    exit(-1);
  }
      
  fseek(fp, 0, SEEK_END);
  posAux = ftell(fp);
      
  /* Seta os intervalos de leitura do arquivo */
  posInit = rank * (posAux/nthreads);
  posEnd = (rank+1) * (posAux/nthreads);
     
  /* Prepara o fim do offset */
  fseek(fp, posEnd, SEEK_SET);
  posEnd = ajustaOffset(fp);
      
  /* Nós filhos devem acertar o valor do offset inicial */
  fseek(fp, posInit, SEEK_SET);
  if(rank > 0){
    posInit = ajustaOffset(fp);
  }
  /*=========================================================================================*/  
    
  /*=========================================================================================*/  
  /* Serão usadas 2 threads */
  omp_set_num_threads(2);
  
  /* Agora lê frase a frase do arquivo, quebrando em palavras */
  while(1){
    /* Lê uma palavra do arquivo */
    get_frase(fp, buffer);
        
    /* Se chegou ao fim do trecho do arquivo que lerá, sai do loop */
    if(ftell(fp) >= posEnd){
      break;
    }
    
    /* Verificação em paralelo de palíndromo em frase e em palavra */
    #pragma omp parallel firstprivate(buffer)
    {
      #pragma omp master
      {
	/* Verifica palíndromo da frase */
	verifica_palindromo(buffer, &partialFrasePalindromos);
      }
      #pragma omp single nowait
      {
	/* Verifica palíndromo em palavras */
	percorre_frase = 0;
	while(percorre_frase < strlen(buffer)){
	  percorre_frase = get_palavras(buffer, palavra, percorre_frase);
	  if(strlen(palavra) > 1){
	    verifica_palindromo(palavra, &partialPalindromos);
	  }
	}
      }
    }  
    /*=========================================================================================*/  
  }
  /*=========================================================================================*/  
    
  /* Fecha o arquivo */
  fclose(fp);
  
  /* Espera todas as threads terminarem */
  MPI_Barrier(MPI_COMM_WORLD);
  
  /* Junta os resultados parciais */
  MPI_Reduce(&partialPalindromos, &globalPalindromos, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
  MPI_Reduce(&partialFrasePalindromos, &globalFrasePalindromos, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
  
  /* Registra o tempo de término */
  end = MPI_Wtime();

  /* Finaliza a parte paralelizada pelo MPI */
  MPI_Finalize();
  
  /* Exibe os resultados */
  if(rank == 0){
    printf("Numero total de frases que são palíndromos = %d.\n", globalFrasePalindromos);
    printf("Numero total palavras que são palíndromos = %d.\n", globalPalindromos);
    printf("Tempo de execucao = %f segundos\n", end-start);
  }
  
  return 0;
}
